var __createBinding =
    (this && this.__createBinding) ||
    (Object.create
        ? (o, m, k, k2) => {
              if (k2 === undefined) k2 = k;
              var desc = Object.getOwnPropertyDescriptor(m, k);
              if (
                  !desc ||
                  ("get" in desc
                      ? !m.__esModule
                      : desc.writable || desc.configurable)
              ) {
                  desc = { enumerable: true, get: () => m[k] };
              }
              Object.defineProperty(o, k2, desc);
          }
        : (o, m, k, k2) => {
              if (k2 === undefined) k2 = k;
              o[k2] = m[k];
          });
var __setModuleDefault =
    (this && this.__setModuleDefault) ||
    (Object.create
        ? (o, v) => {
              Object.defineProperty(o, "default", {
                  enumerable: true,
                  value: v,
              });
          }
        : (o, v) => {
              o.default = v;
          });
var __importStar =
    (this && this.__importStar) ||
    ((mod) => {
        if (mod?.__esModule) return mod;
        var result = {};
        if (mod != null)
            for (var k in mod)
                if (k !== "default" && Object.hasOwn(mod, k))
                    __createBinding(result, mod, k);
        __setModuleDefault(result, mod);
        return result;
    });
var __importDefault =
    (this && this.__importDefault) ||
    ((mod) => (mod?.__esModule ? mod : { default: mod }));
Object.defineProperty(exports, "__esModule", { value: true });
exports.SequenceSplitter = void 0;
const t = __importStar(require("@babel/types"));
const traverse_1 = __importDefault(require("@babel/traverse"));
const transformation_1 = require("../transformation");
class SequenceSplitter extends transformation_1.Transformation {
    /**
     * Executes the transformation.
     * @param log The log function.
     */
    execute(_log) {
        const self = this;
        (0, traverse_1.default)(this.ast, {
            ConditionalExpression(path) {
                if (path.parentPath?.isExpressionStatement()) {
                    const replacement = t.ifStatement(
                        path.node.test,
                        t.expressionStatement(path.node.consequent),
                        t.expressionStatement(path.node.alternate),
                    );
                    if (
                        path.parentPath.parentPath &&
                        path.parentPath.parentPath.key === "alternate" &&
                        path.parentPath.parentPath.isBlockStatement() &&
                        path.parentPath.parentPath.node.body.length === 1
                    ) {
                        path.parentPath.parentPath.replaceWith(replacement);
                    } else {
                        path.parentPath.replaceWith(replacement);
                    }
                    path.skip();
                    self.setChanged();
                }
            },
            LogicalExpression(path) {
                if (
                    (path.node.operator === "&&" ||
                        path.node.operator === "||") &&
                    path.parentPath &&
                    path.parentPath.isExpressionStatement()
                ) {
                    const test =
                        path.node.operator === "&&"
                            ? path.node.left
                            : t.unaryExpression("!", path.node.left);
                    const replacement = t.ifStatement(
                        test,
                        t.expressionStatement(path.node.right),
                    );
                    if (
                        path.parentPath.parentPath &&
                        path.parentPath.parentPath.key === "alternate" &&
                        path.parentPath.parentPath.isBlockStatement() &&
                        path.parentPath.parentPath.node.body.length === 1
                    ) {
                        path.parentPath.parentPath.replaceWith(replacement);
                    } else {
                        path.parentPath.replaceWith(replacement);
                    }
                    path.skip();
                    self.setChanged();
                }
            },
            "ForStatement|WhileStatement|DoWhileStatement"(path) {
                if (!t.isBlockStatement(path.node.body)) {
                    path.node.body = t.blockStatement([path.node.body]);
                    self.setChanged();
                }
            },
            IfStatement(path) {
                if (!t.isBlockStatement(path.node.consequent)) {
                    path.node.consequent = t.blockStatement([
                        path.node.consequent,
                    ]);
                    self.setChanged();
                }
                if (
                    path.node.alternate &&
                    !t.isBlockStatement(path.node.alternate) &&
                    !t.isIfStatement(path.node.alternate)
                ) {
                    path.node.alternate = t.blockStatement([
                        path.node.alternate,
                    ]);
                    self.setChanged();
                }
            },
            VariableDeclaration(path) {
                if (path.node.declarations.length > 1) {
                    const replacements = path.node.declarations.map((d) =>
                        t.variableDeclaration(path.node.kind, [d]),
                    );
                    if (
                        path.parentPath?.isForStatement() &&
                        path.parentKey === "init"
                    ) {
                        const lastDeclaration = replacements.pop();
                        path.parentPath.insertBefore(replacements);
                        path.parentPath.node.init = lastDeclaration;
                    } else {
                        path.replaceWithMultiple(replacements);
                    }
                    self.setChanged();
                }
            },
            SequenceExpression(path) {
                const expressions = path.node.expressions;
                if (expressions.length === 1) {
                    path.replaceWith(expressions[0]);
                    self.setChanged();
                    return;
                }
                let outerPath = path;
                while (!t.isStatement(outerPath.node)) {
                    const parent = outerPath.parentPath;
                    if (!parent) {
                        return;
                    }
                    if (
                        (parent.isConditionalExpression() &&
                            (outerPath.key === "consequent" ||
                                outerPath.key === "alternate")) ||
                        (parent.isLogicalExpression() &&
                            outerPath.key === "right") ||
                        (parent.isForStatement() &&
                            (outerPath.key === "test" ||
                                outerPath.key === "update")) ||
                        (parent.isDoWhileStatement() &&
                            outerPath.key === "test")
                    ) {
                        return;
                    }
                    outerPath = parent;
                }
                const lastExpression = expressions[expressions.length - 1];
                if (self.isExcluded(lastExpression)) {
                    const firstExpressions = expressions.splice(
                        0,
                        expressions.length - 2,
                    );
                    if (firstExpressions.length > 0) {
                        const expressionStatements = firstExpressions.map((e) =>
                            t.expressionStatement(e),
                        );
                        outerPath.insertBefore(expressionStatements);
                        self.setChanged();
                    }
                } else {
                    const lastExpression = expressions.splice(
                        expressions.length - 1,
                        1,
                    )[0];
                    const expressionStatements = expressions.map((e) =>
                        t.expressionStatement(e),
                    );
                    outerPath.insertBefore(expressionStatements);
                    path.replaceWith(lastExpression);
                    self.setChanged();
                }
            },
        });
        return this.hasChanged();
    }
    /**
     * Returns whether a node that is the last in a sequence expression
     * is excluded from being placed on its own.
     * @param node The AST node.
     * @returns Whether.
     */
    isExcluded(node) {
        return t.isIdentifier(node) && node.name === "eval";
    }
}
exports.SequenceSplitter = SequenceSplitter;
SequenceSplitter.properties = {
    key: "sequenceSplitting",
};
